Option Explicit

' ======================================================================
' Descriptor
' 多条件组合
' ======================================================================

' ======================================================================
' Implement
' ======================================================================

Implements ISqlFormatterElem

' ======================================================================
' Field
' ======================================================================

' ==================================================
' Field
' ==================================================

' 保存所有的组成元素
' 只在出现条件对象之外的元素时才会创建
' IObjArrayRef<ISqlFormatterElem>
Private componentArray As IObjArrayRef

' 条件对象集合
' IObjArrayRef<ISqlFormatterElem>
Private conditions As IObjArrayRef

' ==================================================
' ISqlFormatterElem Field
' ==================================================
' 前缀空格的数量
' 默认值 = 0
Private prefixSpaceCount As Integer

' 左侧括号的数量
' 默认值 = 0
Private leftParenthesisCount As Integer
' 右侧括号的数量
' 默认值 = 0
Private rightParenthesisCount As Integer

' 当前对象是否是多行的状态
' 默认值 = false
Private multiLineState As Boolean
' 元素数组是否是以注释结尾
' 默认值 = false
Private componentArrayIsEndsWithComment As Boolean

' ======================================================================
' Constructor
' ======================================================================
Private Sub Class_Initialize()
    ' 组成条件的所有元素
    Set conditions = UtilsArrayRef.createRealObjArrayRef(8)
End Sub

Private Sub Class_Terminate()
    Set conditions = Nothing
End Sub

' ======================================================================
' Public Function
' ======================================================================
' 添加条件
Public Sub addCondition(ByRef c As ISqlFormatterElem)
    ' ==================================================
    ' 1. 检查参数
    ' ==================================================
    If c Is Nothing Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 2. 保存元素
    ' ==================================================
    ' 保存条件元素
    conditions.Add c

    ' 如果 componentArray 已经创建了，则也需要保存
    If Not componentArray Is Nothing Then
        componentArray.Add c
    End If

    ' ==================================================
    ' 3. 设置当前对象的组成元素是不是多行的状态
    ' ==================================================
    ' 1. 检查: 如果当前对象不是多行的状态 `multiLineState == false`，则继续
    If multiLineState = False Then
        ' 2. 检查: 如果当前元素是多行的，则设置 `multiLineState = true`
        If c.isMultiLine Then
            multiLineState = True

        ' 3. 否则，检查: 如果元素数组是以注释结尾的 `componentArrayIsEndsWithComment == true`，则设置 `multiLineState = true`
        ElseIf componentArrayIsEndsWithComment Then
            multiLineState = True

        ' 4. 否则，检查: 如果当前元素是单独一行的注释
        ElseIf c.isSingleLineComment Then
            ' 4.1. 如果元素数组中的元素数量 > 1 ，则设置 `multiLineState = true`
            If componentArray.Count > 1 Then
                multiLineState = True
            End If
        End If
    End If

    ' ==================================================
    ' 4. 保存元素数组是否是以注释结尾的
    ' ==================================================
    ' 1. 将当前元素的是否以注释结尾的结果: `isEndsWithComment() == true` 保存到属性 `componentArrayIsEndsWithComment`
    componentArrayIsEndsWithComment = c.isEndsWithComment
End Sub

' ======================================================================
' implements ISqlFormatterElem Function
' ======================================================================
' ==================================================
' ISqlFormatterElem -- 添加组成元素
' ==================================================
' 添加组成元素
Public Sub ISqlFormatterElem_addComponent(ByRef c As ISqlFormatterElem)
    ' ==================================================
    ' 1. 检查参数
    ' ==================================================
    If c Is Nothing Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 2. 保存元素
    ' ==================================================
    ' 如果集合还没有创建，则进行创建，并吸收所有的条件对象
    If componentArray Is Nothing Then
        Set componentArray = UtilsArrayRef.createRealObjArrayRef(conditions.Count + 8)
        componentArray.mergeArrRef conditions
    End If

    componentArray.Add c

    ' 检查: 如果元素类型是一个条件对象，则添加到条件集合中
    If Typeof c is ISqlFormatterCondition Then
        conditions.Add c
    End If

    With c ' -------> 在这里加速
        ' ==================================================
        ' 3. 设置当前对象的组成元素是不是多行的状态
        ' ==================================================
        ' 1. 检查: 如果当前对象不是多行的状态 `multiLineState == false`，则继续
        If multiLineState = False Then
            ' 2. 检查: 如果当前元素是多行的，则设置 `multiLineState = true`
            If .isMultiLine Then
                multiLineState = True

            ' 3. 否则，检查: 如果元素数组是以注释结尾的 `componentArrayIsEndsWithComment == true`，则设置 `multiLineState = true`
            ElseIf componentArrayIsEndsWithComment = True Then
                multiLineState = True

            ' 4. 否则，检查: 如果当前元素是单独一行的注释
            ElseIf .isSingleLineComment = True Then
                ' 4.1. 如果元素数组中的元素数量 > 1 ，则设置 `multiLineState = true`
                If componentArray.Count > 1 Then
                    multiLineState = True
                End If
            End If
        End If

        ' ==================================================
        ' 4. 保存元素数组是否是以注释结尾的
        ' ==================================================
        ' 1. 将当前元素的是否以注释结尾的结果: `isEndsWithComment() == true` 保存到属性 `componentArrayIsEndsWithComment`
        componentArrayIsEndsWithComment = .isEndsWithComment
    End With
End Sub

' 将元素添加到开头位置
Public Sub ISqlFormatterElem_addComponentToFirst(ByRef c As ISqlFormatterElem)
    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 检查: 如果元素是 `null` 则退出
    If c Is Nothing Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 2. 保存元素
    ' ==================================================
    ' 1. 将元素添加到开头位置
    ' 如果集合还没有创建，则进行创建，并吸收所有的条件对象
    If componentArray Is Nothing Then
        Set componentArray = UtilsArrayRef.createRealObjArrayRef(conditions.Count + 8)
        componentArray.Add c
        componentArray.mergeArrRef conditions
    Else
        componentArray.AddFirst c
    End If

    ' 2. 获取元素数量 和 当前元素是否是以注释结尾
    Dim componentArrayCount As Long
    componentArrayCount = componentArray.Count

    With c ' -------> 在这里加速
        Dim curIsEndsWithComment As Boolean
        curIsEndsWithComment = .isEndsWithComment

        ' ==================================================
        ' 3. 设置当前对象的组成元素是不是多行的状态
        ' ==================================================
        ' 1. 检查: 如果当前对象不是多行的状态 `multiLineState == false`，则继续
        If multiLineState = False Then
            ' 2. 检查: 如果当前元素是多行的，则设置 `multiLineState = true`
            If .isMultiLine Then
                multiLineState = True

            ' 3. 否则，检查: 如果元素数量 > 1
            ElseIf componentArrayCount > 1 Then
                ' 3.1. 检查: 如果当前元素是注释结尾，则是多行
                '    (说明在单行的基础行，又增加了一个包含注释的内容，所以变成了多行)
                If curIsEndsWithComment Then
                    multiLineState = True

                ' 2. 否则，检查: 如果第二个元素是单行注释，则设置为多行的
                Else
                    Dim tempElem As ISqlFormatterElem
                    Set tempElem = componentArray.Item(1)
                    If tempElem.isSingleLineComment Then
                        multiLineState = True
                    End If
                End If
            End If
            ' 4. 否则 (到这里说明只有一个元素，则不用检查，无论是哪种情况都是单行的)
        End If

        ' ==================================================
        ' 4. 如果当前只有一个元素，则保存: 是否是以注释结尾
        ' ==================================================
        If componentArrayCount = 1 Then
            componentArrayIsEndsWithComment = curIsEndsWithComment
        End If
    End With
End Sub

' 能否将元素添加到开头位置
Public Function ISqlFormatterElem_canAddComponentToFirst() As Boolean
    ' 不存在左边界括号时，可以将元素添加到开头
    ISqlFormatterElem_canAddComponentToFirst = (leftParenthesisCount <= 0)
End Function

' ==================================================
' ISqlFormatterElem -- 对齐处理
' ==================================================
' 该方法是用来配置对齐的方法
' 返回元素内，最多一级深度下的对齐元素组
' 如果是列对象，则返回作用域各个组成元素内的条件组（主要是子查询）
' 如果是条件对象，则返回条件自身
' 如果是作用域对象，则返回作用域内各部分中的条件组合
' 如果是赋值语句，则返回语句自身
' 如果没有条件或者赋值语句，则返回 null
'
' @return IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>>
Public Function ISqlFormatterElem_getAlignmentElemGroups() As IObjArrayRef
    ' 如果没有条件，则返回 null
    If conditions.Count = 0 Then
        ' return null
        Exit Function
    End If

    ' IObjArrayRef <ISqlFormatterAlignmentElem> result
    Dim result As IObjArrayRef
    Set result = UtilsArrayRef.createRealObjArrayRef(conditions.Count)

    ' IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>> groups = null
    Dim groups As IObjArrayRef
    ' IObjArrayRef<ISqlFormatterAlignmentElem> group = null
    Dim group As IObjArrayRef

    ' 从 `conditions` 的所有条件元素中获取条件组
    Dim elem As ISqlFormatterElem
    Dim i As Long
    Dim j As Long

    For i = 0 To conditions.MaxIndex
        Set elem = conditions.Item(i)

        ' 1. 检查: 如果当前元素的类型是条件对象，则直接保存到 result
        If Typeof elem Is ISqlFormatterAlignmentElem Then
            result.Add elem

        ' 2. 否则，获取元素内的所有条件组
        Else
            Set groups = elem.getAlignmentElemGroups
            If groups Is Nothing Then
                Goto loopContinue
            End if

            ' 压缩 groups，将每一个分组全部保存到 result
            For j = 0 To groups.MaxIndex
                Set group = groups.Item(j)
                result.mergeArrRef group
            Next
        End If
loopContinue:
    Next

    ' 检查: 如果结果集合的长度为 0，则返回 null
    If result.Count = 0 Then
        ' return null

    ' 否则，将 result 包装为长度为 1 的对象数组，并返回
    Else
        Set ISqlFormatterElem_getAlignmentElemGroups = UtilsArrayRef.wrapperToObjArrayRef(result)
    End If
End Function

' ==================================================
' ISqlFormatterElem -- 前缀空格设置
' ==================================================
' 设置当前元素的嵌套深度，并计算前缀空格的数量
' 会覆盖 setPrefixSpaceCount 的设置结果
Public Sub ISqlFormatterElem_setElemNestCount(ByVal nestCount As Integer, ByRef config As SqlFormatterConfiguration)
    ' ==================================================
    ' 保存当前对象的深度
    ' ==================================================
    prefixSpaceCount = nestCount * config.elemNestLevelPrefixSpaceCount

    ' ==================================================
    ' 计算子条件的深度
    ' ==================================================
    Dim conditionNestCount As Integer
    conditionNestCount = nestCount
    ' 如果存在边界括号，则额外增加一级深度
    If ISqlFormatterElem_hasBoundaryParenthesis = True Then
        conditionNestCount = conditionNestCount + 1
    End If

    ' ==================================================
    ' 设置子条件的深度
    ' ==================================================
    Dim tempCondition As ISqlFormatterCondition
    Dim tempElem As ISqlFormatterElem

    Dim conditionCount As Long
    conditionCount = conditions.Count
    If conditionCount = 1 Then
        ' 只有一个条件对象，则只设置: 嵌套深度
        Set tempElem = conditions.Item(0)
        tempElem.setElemNestCount conditionNestCount, config
    Else
        ' 存在多个条件对象时，设置所有条件对象的: 嵌套深度 + 非唯一性
        Dim i As Long
        For i = 0 To conditionCount - 1
            Set tempElem = conditions.Item(i)
            tempElem.setElemNestCount conditionNestCount, config

            If TypeOf tempElem Is ISqlFormatterCondition Then
                Set tempCondition = tempElem
                tempCondition.setNotOnlyOneCondition
            End If
        Next
    End If

End Sub

' 直接设置前缀空格的数量
' 将会覆盖 setElemNestCount 对前缀空格数量的控制
Public Sub ISqlFormatterElem_setPrefixSpaceCount(ByVal spaceCount As Integer, ByRef config As SqlFormatterConfiguration)
    ' ==================================================
    ' 保存当前对象的前缀空格数量
    ' ==================================================
    prefixSpaceCount = spaceCount

    ' ==================================================
    ' 计算子条件的前缀空格数量
    ' ==================================================
    Dim conditionSpaceCount As Integer
    conditionSpaceCount = spaceCount

    ' 如果存在边界括号，则额外增加一级深度
    If ISqlFormatterElem_hasBoundaryParenthesis = True Then
        conditionSpaceCount = conditionSpaceCount + config.elemNestLevelPrefixSpaceCount
    End If

    ' ==================================================
    ' 设置子条件的前缀空格数量 和 非唯一性
    ' ==================================================
    Dim tempCondition As ISqlFormatterCondition
    Dim tempElem As ISqlFormatterElem

    Dim conditionCount As Long
    conditionCount = conditions.Count

    If conditionCount = 1 Then
        ' 只有一个条件对象，则只设置: 前缀空格数量
        Set tempElem = conditions.Item(0)
        tempElem.setPrefixSpaceCount conditionSpaceCount, config
    Else
        ' 存在多个条件对象时，设置所有条件对象的: 前缀空格数量 + 非唯一性
        Dim i As Long
        For i = 0 To conditionCount - 1
            Set tempElem = conditions.Item(i)
            tempElem.setPrefixSpaceCount conditionSpaceCount, config

            If TypeOf tempElem Is ISqlFormatterCondition Then
                Set tempCondition = tempElem
                tempCondition.setNotOnlyOneCondition
            End If
        Next
    End If

End Sub

' ==================================================
' ISqlFormatterElem -- 边界括号
' ==================================================
' 是否存在边界括号，有左边或者右边都返回 true
Public Function ISqlFormatterElem_hasBoundaryParenthesis() As Boolean
    If leftParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    ElseIf rightParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    End If

    ' return False
End Function

' 边界处的 [(] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryLeftParenthesis(Optional ByVal count As Integer = 1)
    leftParenthesisCount = leftParenthesisCount + count
End Sub

' 边界处的 [)] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryRightParenthesis(Optional ByVal count As Integer = 1)
    rightParenthesisCount = rightParenthesisCount + count
End Sub

' ==================================================
' ISqlFormatterElem -- 内容检查
' ==================================================
' 获取类型
Public Function ISqlFormatterElem_getType() As SqlTokenType
    ISqlFormatterElem_getType = SqlTokenType.NO_MEAN
End Function

' 检查当前元素是不是一个注释
Public Function ISqlFormatterElem_isComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是和前一个元素处于同一行的注释
Public Function ISqlFormatterElem_isShareLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是处于单独一行的注释
Public Function ISqlFormatterElem_isSingleLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是以注释结尾
Public Function ISqlFormatterElem_isEndsWithComment() As Boolean
    ' 1. 如果存在右括号，则默认不是以注释结尾
    If rightParenthesisCount > 0 Then
        ' return false
        Exit Function
    End If

    ' 2. 返回元素数组是否以注释结尾
    ISqlFormatterElem_isEndsWithComment = componentArrayIsEndsWithComment
End Function

' 检查当前生成器是不是多行的
Public Function ISqlFormatterElem_isMultiLine() As Boolean
    ' 检查: 如果已经是多行的，则返回 true
    If multiLineState Then
        ISqlFormatterElem_isMultiLine = True
        Exit Function
    End IF

    ' 如果存在多个条件，则返回 true
    If conditions.Count > 1 Then
        ISqlFormatterElem_isMultiLine = True
        Exit Function
    End If

    ' 如果元素数组存在，并且元素数量 > 1，则返回 true
    If Not componentArray Is Nothing Then
        Dim componentArrayCount As Integer
        componentArrayCount = componentArray.Count

        ' 检查: 如果只有一个元素，则返回 false
        If componentArrayCount = 1 Then
            ' return false
            Exit Function

        ' 否则，检查: 如果有两个元素
        ElseIf componentArrayCount = 2 Then
            ' 1. 检查: 如果第一个元素是注释结尾，则返回 true
            Dim elem As ISqlFormatterElem
            Set elem = componentArray.Item(0)
            If elem.isEndsWithComment Then
                ISqlFormatterElem_isMultiLine = True
                Exit Function
            End If

            ' 2. 检查: 如果第二个元素不是和前一个元素同一行的注释，则返回 true
            Set elem = componentArray.Item(1)
            If elem.isShareLineComment = False Then
                ISqlFormatterElem_isMultiLine = True
                Exit Function
            End If

            ' 3. 检查：如果存在边界括号，则返回 true
            ' 4. 否则，返回 false (单行元素 + 同行注释 + 无边界括号)
            ISqlFormatterElem_isMultiLine = ISqlFormatterElem_hasBoundaryParenthesis
            Exit Function

        ' 否则，检查: 如果数量 > 2，则返回 true
        Else
            ISqlFormatterElem_isMultiLine = True
            Exit Function
        End If
    End If

    ' 检查: 如果最后一个组成元素以注释结尾，则继续
    If componentArrayIsEndsWithComment Then
        ' 如果最后一个组成元素是以注释结尾，则如果还存在边界括号，就是多行的
        ISqlFormatterElem_isMultiLine = ISqlFormatterElem_hasBoundaryParenthesis
    End If

    ' 否则: 返回 false
    ' return false
End Function

' ==================================================
' ISqlFormatterElem -- 生成列的字符串
' ==================================================
' 生成一整行的字符串。如果本身是多行，将会使用换行符进行连接
Public Function ISqlFormatterElem_toString(ByRef config As SqlFormatterConfiguration) As String
    If ISqlFormatterElem_isMultiLine = False Then
        ' 1. 如果是单行的，则直接生成单行字符串
        ISqlFormatterElem_toString = doCreateSingleLineString(config)
        Exit Function
    Else
        ' 1. 生成原始字符集合
        Dim arrRef As IPrimitiveArrayRef
        Set arrRef = doCreateMultiLineArray(config)
        ' 2. 使用换行符连接
        ISqlFormatterElem_toString = UtilsArrayRef.reducePrimitiveArrayRefToStr(arrRef, config.lineBreak)
        Exit Function
    End If
End Function

' 生成多行字符串
' 如果只有一行，则返回的数组引用中将会只有一个
' 并且不会在开头附加前缀空格
'
' @return IPrimitiveArrayRef<String>
Public Function ISqlFormatterElem_toStringArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef

    ' ==================================================
    ' 1. 处理单行的情况
    ' ==================================================
    ' 1. 检查: 如果是单行的，则生成单行字符串
    If ISqlFormatterElem_isMultiLine = False Then
        Dim singleStr As String
        singleStr = doCreateSingleLineString(config)

        ' 2. 将 `singleStr` 包装成一个长度为 1 的结果集合，并返回
        Set ISqlFormatterElem_toStringArray = UtilsArrayRef.wrapperToPrimitiveArrayRef(singleStr)
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理多行的情况
    ' ==================================================
    ' 1. 生成多行字符串集合，返回生成结果
    Set ISqlFormatterElem_toStringArray = doCreateMultiLineArray(config)
End Function

' ======================================================================
' Private Function
' ======================================================================
' 生成单行字符串
Private Function doCreateSingleLineString(ByRef config As SqlFormatterConfiguration) As String
    ' 1. 检查: 如果元素数组存在: `componentArray != null`，为 `componentArray` 生成字符串，并返回
    If Not componentArray Is Nothing Then
        doCreateSingleLineString = SqlFormatterHelper.createFormatSingleStr( _
            componentArray, config, leftParenthesisCount, rightParenthesisCount)
        Exit Function
    End If

    doCreateSingleLineString = SqlFormatterHelper.createFormatSingleStr( _
        conditions, config, leftParenthesisCount, rightParenthesisCount)
End Function

' 获取原始的字符串集合
' 不包含前缀空格
' 不包含换行符，所以在产生多行的字符串时，返回一个数组集合
' @return IPrimitiveArrayRef<String>
Private Function doCreateMultiLineArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' 1. 检查: 如果元素数组存在: `componentArray != null`
    If Not componentArray Is Nothing Then
        ' 1.1. 为 `componentArray` 生成字符串集合 `result`
        Set doCreateMultiLineArray = SqlFormatterHelper.createElemExclusiveRowStrArr( _
            componentArray, config, leftParenthesisCount, rightParenthesisCount)

        ' 1.2. 返回 `result`
        Exit Function
    End If

    ' 2. 否则
    ' 2.1. 为 `conditions` 生成字符串集合
    Set doCreateMultiLineArray = SqlFormatterHelper.createElemExclusiveRowStrArr( _
        conditions, config, leftParenthesisCount, rightParenthesisCount)

    ' 2.2. 返回 `result`
    ' return result
End Function